<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/timing.html">
<link rel="import" href="/tracing/value/histogram_grouping.html">
<link rel="import" href="/tracing/value/histogram_set.html">

<script>
'use strict';
tr.exportTo('tr.v', function() {
  const getDisplayLabel =
    tr.v.HistogramGrouping.DISPLAY_LABEL.callback;

  const DEFAULT_POSSIBLE_GROUPS = [];

  const EXCLUDED_GROUPING_KEYS = [
    tr.v.HistogramGrouping.DISPLAY_LABEL.key,
  ];
  // HISTOGRAM_NAME is overridden.
  // DISPLAY_LABEL is used to define the columns, so don't allow grouping rows
  // by it.
  for (const group of tr.v.HistogramGrouping.BY_KEY.values()) {
    if (EXCLUDED_GROUPING_KEYS.includes(group.key)) continue;
    DEFAULT_POSSIBLE_GROUPS.push(group);
  }

  // This Processor collects various parameters from a set of Histograms such as
  // their statistics, displayLabels, and grouping keys in a single pass.
  class HistogramParameterCollector {
    constructor() {
      this.statisticNames_ = new Set(['avg']);

      this.labelsToStartTimes_ = new Map();

      // @typedef {!Map.<string,!tr.v.HistogramGrouping>}
      this.keysToGroupings_ = new Map(DEFAULT_POSSIBLE_GROUPS.map(
          g => [g.key, g]));

      // Map from HistogramGrouping keys to Sets of return values from the
      // HistogramGroupings' callbacks.
      this.keysToValues_ = new Map(DEFAULT_POSSIBLE_GROUPS.map(
          g => [g.key, new Set()]));

      // Never remove 'name' from keysToGroupings.
      this.keysToValues_.delete(
          tr.v.HistogramGrouping.HISTOGRAM_NAME.key);
    }

    process(histograms) {
      const allStoryTags = new Set();
      let maxSampleCount = 0;
      for (const hist of histograms) {
        maxSampleCount = Math.max(maxSampleCount, hist.numValues);

        for (const statName of hist.statisticsNames) {
          this.statisticNames_.add(statName);
        }

        let startTime = hist.diagnostics.get(
            tr.v.d.RESERVED_NAMES.BENCHMARK_START);
        if (startTime !== undefined) startTime = startTime.minDate.getTime();

        const displayLabel = getDisplayLabel(hist);

        if (this.labelsToStartTimes_.has(displayLabel)) {
          startTime = Math.min(startTime,
              this.labelsToStartTimes_.get(displayLabel));
        }
        this.labelsToStartTimes_.set(displayLabel, startTime);

        for (const [groupingKey, values] of this.keysToValues_) {
          const grouping = this.keysToGroupings_.get(groupingKey);
          const value = grouping.callback(hist);
          if (!value) continue;
          values.add(value);
          if (values.size > 1) {
            // This grouping will definitely stay in keysToGroupings_. We don't
            // need to see any more values in the rest of histograms. Remove
            // this groupingKey from this.keysToValues_ so that we don't compute
            // it for any more histograms and so that we don't delete it from
            // keysToGroupings_.
            this.keysToValues_.delete(groupingKey);
          }
        }

        const storyTags = hist.diagnostics.get(
            tr.v.d.RESERVED_NAMES.STORY_TAGS);
        for (const tag of (storyTags || [])) {
          allStoryTags.add(tag);
        }
      }
      tr.b.Timing.instant(
          'HistogramParameterCollector', 'maxSampleCount', maxSampleCount);

      for (const tagGrouping of tr.v.HistogramGrouping.buildFromTags(
          allStoryTags, tr.v.d.RESERVED_NAMES.STORY_TAGS)) {
        const values = new Set();
        for (const hist of histograms) {
          values.add(tagGrouping.callback(hist));
        }
        if (values.size > 1) {
          this.keysToGroupings_.set(tagGrouping.key, tagGrouping);
          this.keysToValues_.set(tagGrouping.key, values);
        }
      }

      this.statisticNames_.add('pct_090');
    }

    get statisticNames() {
      return Array.from(this.statisticNames_);
    }

    get labels() {
      const displayLabels = Array.from(this.labelsToStartTimes_.keys());
      displayLabels.sort((x, y) =>
        this.labelsToStartTimes_.get(x) - this.labelsToStartTimes_.get(y));
      return displayLabels;
    }

    get possibleGroupings() {
      for (const [key, values] of this.keysToValues_) {
        if (values.size >= 2) continue;
        // Remove this grouping from keysToGroupings_ if there is fewer than
        // 2 possible values.
        this.keysToGroupings_.delete(key);
      }

      return Array.from(this.keysToGroupings_.values());
    }
  }

  return {
    HistogramParameterCollector,
  };
});
</script>
